package com.hexb.core.web;


import com.hexb.core.common.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;

/**
 * <h4>为restController返回json自动包装ResponseResult</h4>
 * <p>经过此方法处理对象默认为正确返回的结果,异常情况请使用throw new BusinessException方式返回</p>
 *
 * @author hexb
 */
public class ResponseResultMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    static private final String SWAGGER_DOC_PREFIX = "springfox.documentation";

    static private final String FEIGN_OUTPUT_MESSAGE = "FeignOutputMessage";

    private String warpExclude = "";

    @Autowired(required = false)
    private ResponseWrapFilter responseWrapFilter;

    public ResponseResultMappingJackson2HttpMessageConverter() {
        super();
    }

    public ResponseResultMappingJackson2HttpMessageConverter(String warpExclude) {
        super();
        this.warpExclude = warpExclude;
    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        //修复:Feign RequestBody请求时被包装问题
        if (outputMessage != null && FEIGN_OUTPUT_MESSAGE.equals(outputMessage.getClass().getSimpleName())) {
            super.writeInternal(object instanceof DataNull ? null : object, type, outputMessage);
        }

        //是否需要在数据外部包装ResponseResult,数据作为ResponseResult.data
        boolean needWrap = true;

        //排除swagger包产生的json
        //排除已经被包装为ResponseResult的对象，如异常产生的ResponseResult或RawResponseResult
        if (type instanceof Class) {
            Class<?> clazz = (Class) type;
            if (ResponseResult.class.isAssignableFrom(clazz)
                    || isExclude(clazz.getName())
                    || (responseWrapFilter != null && responseWrapFilter.exclude(object, type))) {
                needWrap = false;
            }
        } else if (type instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) type;
            Type[] actualTypeArguments = pType.getActualTypeArguments();
            if (null != actualTypeArguments && actualTypeArguments.length > 0) {
                Type actualType = actualTypeArguments[0];
                if (null != actualType) {
                    if (actualType instanceof Class) {
                        Class clazz = (Class) actualType;
                        if (clazz.getName().startsWith(SWAGGER_DOC_PREFIX)) {
                            needWrap = false;
                        }
                    }
                }
            }
        }

        if (needWrap) {
            ResponseResult responseEntity = ResponseResult.successful(object instanceof DataNull ? null : object);
            super.writeInternal(responseEntity, type, outputMessage);
        } else {
            super.writeInternal(object instanceof DataNull ? null : object, type, outputMessage);
        }
    }

    private Boolean isExclude(String className) {
        if (className.startsWith(SWAGGER_DOC_PREFIX)) {
            return true;
        }
        if (!StringUtils.isEmpty(warpExclude)) {
            long count = Arrays.asList(warpExclude.split(",")).stream().filter(s -> className.startsWith(s)).count();
            if (count > 0) {
                return true;
            }
        }
        return false;
    }
}
